home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / GameKit / gamekit-1 / GameBrain.m < prev    next >
Text File  |  1995-06-12  |  16KB  |  504 lines

  1. /* Generated by Interface Builder */
  2.  
  3. #import <gamekit/gamekit.h>
  4. #import <daymisckit/daymisckit.h>
  5. #import <stdio.h>
  6. #import <string.h>
  7. #import <objc/objc-runtime.h>
  8.  
  9. @implementation GameBrain
  10.  
  11. - init        // designated initializer sets up game variables
  12. {        // to sensible values.
  13.     [super init];
  14.     level = 0;
  15.     paused = NO;
  16.     ranOnce = NO;
  17.     aborting = NO;
  18.     printLevel = NO;
  19.     initDone = NO;
  20.     return self;
  21. }
  22.  
  23. /* methods to get at important variables */
  24. - highScoreController { return highScoreController; }
  25. - oneUpView { return oneUpView; }
  26. - scoreKeeper { return scoreKeeper; }
  27. - scorePlayer { return scorePlayer; }
  28. - soundPlayer { return soundPlayer; }
  29. - mainStrings { return strings; }
  30. - gameScreen { return gameScreen; }
  31. - gameWindow { return gameWindow; }
  32. - preferencesBrain { return preferencesBrain; }
  33. - infoController { return infoController; }
  34. - gameInfo { return gameInfo; }
  35.  
  36. - (BOOL)aborting { return aborting; }
  37. - makeGameInfo { return [[GameInfo alloc] init]; }
  38. - (int)startLevel { return [preferencesBrain startLevel]; }
  39. - (int)tableNum { return tableNum; }
  40. - (int)level { return level; }
  41. - (int)speed { return [preferencesBrain speed]; }
  42. - (int)paused { return paused; }
  43. - (BOOL)playerCheated { return playerCheated; }
  44. - gameOver:sender        // end the game, take high scores, etc.
  45. {                // calls gameOver method (this is IB wrapper)
  46.     [self gameOver];
  47.     return self;
  48. }
  49.  
  50. - pauseGame:sender        // toggle pause status of the game
  51. {
  52.     const char *title = [pauseMenuCell title];
  53.     
  54.     if (!strcmp(title,[strings valueForStringKey:"Pause"])) {
  55.         [self pause];
  56.     } else {
  57.         [self unpause];
  58.     } 
  59.     return self;
  60. }
  61.  
  62. - (int)pause                // pause game  
  63. {
  64.     if ([gameScreen gameState]==GAMEOVER) return NO; // no pausing if over
  65.     [gameTimer pauseTiming:self];
  66. #ifdef NOISYDEBUG
  67.     fprintf(stderr, "Pause timer, elapsed time is %s\n",
  68.             [gameTimer stringValue]);
  69. #endif
  70.     [pauseMenuCell setTitle:[strings valueForStringKey:"Unpause"]];
  71.     [gameScreen pause:self];
  72.     if (!paused)
  73.         [gameWindow setTitle:[strings valueForStringKey:"PausedTitle"]];
  74.     paused = YES;
  75.     return YES;
  76. }
  77.  
  78. - unpause            // unpause game  
  79. {
  80.     if ([gameScreen gameState]==GAMEOVER) [pauseMenuCell setEnabled:NO];
  81.     [pauseMenuCell setTitle:[strings valueForStringKey:"Pause"]];
  82.     [gameScreen unpause:self];
  83.     [gameWindow setTitle:[strings valueForStringKey:"GameName"]];
  84.     paused = NO;
  85. #ifdef NOISYDEBUG
  86.     fprintf(stderr, "Unpause game timer\n");
  87. #endif
  88.     [gameTimer continueTiming:self];
  89.     return self;
  90. }
  91.  
  92. - startNewGame:sender        // starts a new game
  93. {
  94.     if (![self askAbortGame:GK_ABORT]) return nil;
  95.     if ([gameScreen gameState] != GAMEOVER) {
  96.         aborting = YES;
  97.         [self gameOver];
  98.     }
  99.     [scoreKeeper resetScore];
  100.     [scoreKeeper updateTopScoreText];
  101.     [scoreKeeper updateScoreText];
  102.     level = 0;
  103.     playerCheated = NO;
  104.     ranOnce = YES;
  105.     [pauseMenuCell setEnabled:YES];
  106.     [self layerWindows];    
  107.     level = [preferencesBrain startLevel] - 1;  // so all level init is done
  108.     [preferencesBrain startingGame];
  109.     [oneUpView setNumUp:[gameInfo numOneUps]];
  110.     [self nextLevel];   // by the nextLevel method.
  111.     if (paused) [self unpause];
  112.     [gameWindow makeKeyAndOrderFront:self];
  113.     // use sender's tag to determine which HS table to use. (Expert, Beg., etc)
  114.     tableNum = 0; // should be the prefs default here *****
  115.     if ([sender respondsTo:@selector(tag)]) {
  116.         int theTag = [sender tag];
  117.         if (theTag > 0) tableNum = theTag;
  118.     }
  119.     //[highScoreController table:tableNum]; // get applicable high score table
  120.     currentSlot = [self buildNewSlot];
  121. #ifdef NOISYDEBUG
  122.     fprintf(stderr, "Starting time is:  %s\n",
  123.             [[currentSlot startTime] stringValue]);
  124. #endif
  125.     [gameTimer startTiming:self];
  126.     [gameScreen restartGame];
  127. #ifdef GK_USE_MUSICKIT
  128.     if ([preferencesBrain music] && ([gameScreen gameState] != GAMEOVER))
  129.         [scorePlayer play:self];
  130. #endif
  131.     aborting = NO;
  132.     return self;
  133. }
  134.  
  135. - unpauseGame:sender        // unpause the game 
  136. {
  137.     return [self pauseGame:sender]; // handles toggle of menuCell
  138. }
  139.  
  140. - nextLevel        // move to next level-- called when level is
  141.                 // completed and to start game
  142. {
  143.     level++;
  144.     [levelText setIntValue:level];
  145.     [gameScreen setUpScreen];
  146.     return self;
  147. }
  148.  
  149. - nextLevel:sender    // this is can be called from the interface; it considers
  150. // the user to have cheated since they haven't completed the level normally.
  151. {
  152.     playerCheated = YES;
  153.     return [self nextLevel];
  154. }
  155.  
  156. - buildNewSlot
  157. {    // build a high score slot at the start of a game.
  158.     id newSlot;
  159.     if (gameInfo) {    // use class in GameInfo...must be linked in already!
  160.         newSlot = [[objc_lookUpClass([[gameInfo slotType] stringValue])
  161.                 alloc] init];
  162.     } else newSlot = [[HighScoreSlot alloc] init];
  163.     gameTimer = [[DAYStopwatch alloc] init];
  164.     [newSlot setElapsedTime:gameTimer];
  165.     [newSlot setStartTime:[[DAYTime alloc] initWithCurrentTime]];
  166.     [newSlot setPlayerName:[preferencesBrain defaultPlayerName]];
  167.     [newSlot setUserName:[NXApp userLoginName]];
  168.     [newSlot setMachineName:[NXApp realHostName]];
  169.     [newSlot setStartLevel:[preferencesBrain startLevel]];
  170.     [newSlot setEndLevel:[preferencesBrain startLevel]];
  171.     
  172.     return newSlot;
  173. }
  174.  
  175. - currentHighScoreSlot
  176. {
  177.     // update the slot and return it.
  178.     [currentSlot setEndLevel:level];
  179.     if ([gameScreen gameState] != GAMEOVER)
  180.         [currentSlot setEndTime:[[DAYTime alloc] initWithCurrentTime]];
  181.     [currentSlot setFinalScore:[scoreKeeper currentScore]];
  182.     [gameTimer calcElapsedTime:self];
  183. #ifdef NOISYDEBUG
  184.     fprintf(stderr, "Current elapsed time is:  %s\n", [gameTimer stringValue]);
  185. #endif
  186.     return currentSlot;
  187. }
  188.  
  189. - gameOver            // tidy up game, allow high score name entry
  190.                 
  191. {
  192.     [gameTimer pauseTiming:self]; // stop the timer now that the game is over
  193.     //    implies a -calcElapsedTime: message, so we don't send it from here
  194.     [currentSlot setEndTime:[[DAYTime alloc] initWithCurrentTime]];
  195. #ifdef NOISYDEBUG
  196.     fprintf(stderr, "Ending elapsed time is:  %s\n", [gameTimer stringValue]);
  197.     fprintf(stderr, "Ending time is:  %s  (%s elapsed)\n",
  198.             [[currentSlot endTime] stringValue],
  199.             [[currentSlot elapsedTime] stringValue]);
  200. #endif
  201.     gameTimer = nil;
  202.     // remove demo mode title...
  203.     if ([gameScreen demoMode])
  204.             [gameWindow setTitle:[strings valueForStringKey:"GameName"]];
  205.     // if not demo, it's possible to get a high score entry...
  206.     else if ([gameScreen gameState] != GAMEOVER)
  207.         [highScoreController putInHighScores:[self currentHighScoreSlot]];
  208.     [pauseMenuCell setEnabled:NO];
  209. #ifdef GK_USE_MUSICKIT
  210.     [scorePlayer stop:self];
  211. #endif
  212.     currentSlot = nil;
  213.     [gameScreen gameOver]; // change gameView's state to GAMEOVER
  214.     if (paused) [self unpause];
  215.     return self;
  216. }
  217.  
  218. - printGame:sender        // print game screen w/score, level, etc.
  219. {
  220.     char titleString[80];
  221.     
  222.     if (printLevel) sprintf(titleString,
  223.             [strings valueForStringKey:"PrintTitleLevel"], level,
  224.             [scoreKeeper currentScore], [highScoreController highestScore]);
  225.     else sprintf(titleString,[strings valueForStringKey:"PrintTitle"],
  226.             [scoreKeeper currentScore], [highScoreController highestScore]);
  227.     [self pause]; // bringing up the print panel will do this anyway,
  228.     // indirectly, via the delegate methods, but that would screw up the
  229.     // window title bar we want to put up here:  (score, highest score)
  230.     [gameWindow setTitle:titleString];
  231.     [gameWindow smartPrintPSCode:self];
  232.     if (paused)
  233.         [gameWindow setTitle:[strings valueForStringKey:"PausedTitle"]];
  234.     else [gameWindow setTitle:[strings valueForStringKey:"GameName"]];
  235.     return self;
  236. }
  237.  
  238. //  Application DELEGATE methods.  Special things to do on startup, unhide,
  239. //  hide, and so on.
  240.  
  241. - buildAlert
  242. {
  243.     if (!alert) alert = NXGetAlertPanel([strings valueForStringKey:"GameName"],
  244.         [strings valueForStringKey:"LoadingMessage"], NULL, NULL, NULL);
  245.     return self;
  246. }
  247.  
  248. - appWillInit:sender        // after init, but before 1st event.
  249. {
  250.     [self buildAlert];
  251.     [alert makeKeyAndOrderFront:self];
  252.     [loadingText setStringValue:[strings valueForStringKey:"LoadInit"]];
  253.     NXPing();
  254.     return self;
  255. }
  256.  
  257. - appDidInit:sender        // after init, but before 1st event.
  258. {
  259.     NXEvent *theEvent = (NXEvent *)malloc(sizeof(NXEvent));
  260.     
  261.     if (!gameInfo) gameInfo = [self makeGameInfo];
  262.     [pauseMenuCell setEnabled:NO];
  263.     if (alert != loadingPanel) [alert orderOut:self];
  264.     // methods to load the stuff that takes a while.
  265.     [loadingPanel makeKeyAndOrderFront:self];
  266.  
  267.     // automatically make a bunch of the needed connections if they
  268.     // haven't already been made in the .nib file and inform the
  269.     // main objects that the app is up and running.
  270.     if (!gameWindow) gameWindow = [gameScreen window];
  271.     if (![gameWindow delegate]) [gameWindow setDelegate:self];
  272.     [infoController appDidInit:sender];
  273.  
  274.     [loadingText setStringValue:[strings valueForStringKey:"LoadPrefs"]];
  275.     NXPing();
  276.     [preferencesBrain appDidInit:sender];
  277.     [[levelText window] setFrameUsingName:"Stats"];
  278.     [[levelText window] setFrameAutosaveName:"Stats"];
  279.     [gameWindow setFrameUsingName:"Game"];
  280.     [gameWindow setFrameAutosaveName:"Game"];
  281.  
  282.     [loadingText setStringValue:[strings valueForStringKey:"LoadSound"]];
  283.     NXPing();
  284.     [soundPlayer appDidInit:sender];
  285.  
  286.     [loadingText setStringValue:[strings valueForStringKey:"LoadImages"]];
  287.     NXPing();
  288.     [gameScreen appDidInit:sender];
  289.  
  290. #ifdef GK_USE_MUSICKIT
  291.     [loadingText setStringValue:[strings valueForStringKey:"LoadScore"]];
  292.     NXPing();
  293.     [scorePlayer appDidInit:sender];
  294. #endif
  295.  
  296.     [loadingText setStringValue:[strings valueForStringKey:"LoadHighs"]];
  297.     NXPing();
  298.     [highScoreController appDidInit:sender];
  299.     [levelText setIntValue:1];
  300.     [scoreKeeper appDidInit:sender];
  301.     if (oneUpView) [scoreKeeper addDelegate:oneUpView];
  302.  
  303.     [loadingPanel orderOut:self];
  304.     [[gameScreen animate:self] update];        // start up animation
  305.     if ((alert != loadingPanel) && alert) NXFreeAlertPanel(alert);
  306.     
  307.     // the app-defined event will be the first one, used to bring up the
  308.     // various panels that may turn up during the start up sequence.
  309.     // (i.e. welcome, readme, shareware alert.)
  310.     theEvent->type = NX_APPDEFINED;
  311.     theEvent->data.compound.subtype = GK_STARTUP;
  312.     DPSPostEvent(theEvent, YES);    // post as the next event to be serviced.
  313.     return self;
  314. }
  315.  
  316. - startUp    // handle the startup app-defined event
  317. {
  318.     BOOL firstTimeRun = [preferencesBrain firstTimeCheck];
  319.     if ([infoController notRegistered]) { // shareware alert always...
  320.         NXRunAlertPanel(
  321.                 [strings valueForStringKey:"SharewareAlert"],
  322.                 [strings valueForStringKey:"SharewareMessage"],
  323.                 [strings valueForStringKey:"Understand"], NULL, NULL);
  324.     }
  325.     if (!firstTimeRun && [preferencesBrain autoStart])
  326.         [self startNewGame:self];
  327.     [self layerWindows];
  328.     if (firstTimeRun) { // welcome and readme
  329.         char *str = malloc(128);
  330.         sprintf(str, [strings valueForStringKey:"Welcome1"],
  331.                 [infoController versionString]);
  332.         NXRunAlertPanel([strings valueForStringKey:"Welcome"],
  333.                 str, [strings valueForStringKey:"LetsPlay"],
  334.                 NULL, NULL);
  335.         free(str);
  336.         [infoController readme:self];
  337.     }
  338.     initDone = YES;
  339.     return self;
  340. }
  341.  
  342. - applicationDefined:(NXEvent *)theEvent    // initial startup readme panels
  343. { // a subclass could add other event types, but don't forget to call super!
  344.     // You'd probably want to structure your code more or less like what
  345.     // you see here (switch), if you have several event types declared.
  346.     switch (theEvent->data.compound.subtype) { // switch allows me to
  347.         // easily add more event types if I need to do so.
  348.         case GK_STARTUP : { [self startUp]; break; }
  349.         default : { break; }
  350.     }
  351.     return self;
  352. }
  353.  
  354. - appDidBecomeActive:sender
  355. {    // why the "initDone" ivar?
  356.     // don't want to order the window up until we've got return values from
  357.     // the alert panels that turn up when the game starts.  Since this message
  358.     // is sent after the alert comes up but before the user replies (this
  359.     // message is sent when the event loop becomes active, including launch)
  360.     // we need to NOT order the gamewindow, etc. out until the start up
  361.     // sequence is complete.  This is a hack that removes an annoying bug
  362.     // that occured with panels popping up and making a general mess of the
  363.     // screen...since in 2.x this message was _NOT_ sent on the initial
  364.     // launch's activation of the event loop.  The 3.x behavior is more
  365.     // correct, I suppose, but it was annoying to track down why this was
  366.     // happening.  Anyway, this works around the problem just fine.
  367.     if (initDone) [self layerWindows];
  368.     return self;
  369. }
  370.  
  371. - appDidHide:sender
  372. {
  373.     [self pause];
  374.     return self;    // pause game on Command-h
  375. }
  376.  
  377. - appDidResignActive:sender
  378. {
  379.     if ([gameScreen demoMode]) return self;
  380.     [self pause];
  381.     return self;    // pause game on app deactivate
  382. }
  383.  
  384. - layerWindows
  385. {
  386.     [gameWindow makeKeyAndOrderFront:self];
  387.     [gameWindow makeFirstResponder:gameScreen];
  388.     if ([preferencesBrain autoUnPause]) [self unpause];
  389.     // make sure the windows are layered properly
  390.     // need to check delegate to be sure it responds...
  391.     // Note:  NeXT has said, for portability, that we shouldn't depend on
  392.     //     a method to return zero if sent to a nil object unless that method
  393.     //    returns an id, in which case nil will be "returned".  Thus, the
  394.     //    first half of the condition below has been put in to deal with
  395.     //    when there's no delegate.  It's not really needed on Intel or Moto
  396.     //    hardware, but I'm keeping later ports in mind here, too...
  397.     if (![[levelText window] delegate] || // if no delegate, assume window up
  398.             [[[levelText window] delegate] windowUp])
  399.         [[levelText window] orderFront:self];
  400.     [gameWindow orderFront:self];
  401.     return self;
  402. }
  403.  
  404. - appDidUnhide:sender
  405. {
  406.     return [self layerWindows];
  407. }
  408.  
  409. - appWillTerminate:sender        // update DEFAULTS here 
  410. {
  411.     [preferencesBrain writeDefaults:self];
  412.     return self;
  413. }                    
  414.  
  415. - (BOOL)askAbortGame:(int)why
  416. {
  417.     if (([gameScreen gameState] != GAMEOVER) && (![gameScreen demoMode]) &&
  418.             ([preferencesBrain alert])) {
  419.         BOOL flag = (why == GK_EXIT);
  420.         // Verify that player wants to leave game
  421.         [self pause];
  422.         if (NXRunAlertPanel(([highScoreController
  423.                 slotIsEligible:[self currentHighScoreSlot]] ?
  424.                     [strings valueForStringKey:"HaveScore"] : NULL),
  425.                 (flag ? [strings valueForStringKey:"ReallyQuit"] :
  426.                     [strings valueForStringKey:"ThrowAway"]),
  427.                 (flag ? [strings valueForStringKey:"Yes2"] :
  428.                     [strings valueForStringKey:"Yes1"]),
  429.                 (flag ? [strings valueForStringKey:"No2"] :
  430.                     [strings valueForStringKey:"No1"]),
  431.                 NULL) != NX_ALERTDEFAULT) {
  432.         [self unpause];    // unpause if we're aborting the game abort. :->
  433.         return NO;
  434.     }    }
  435.     return YES;
  436. }
  437.  
  438. - abortGame:sender
  439. {
  440.     if ([self askAbortGame:GK_ABORT]) {
  441.         aborting = YES;
  442.         [self gameOver];
  443.     }
  444.     return self;
  445. }
  446.  
  447. - quit:sender
  448. {
  449.     if (![self askAbortGame:GK_EXIT]) return self;
  450.     if ([infoController notRegistered]) {
  451.         if (ranOnce) {
  452.             NXRunAlertPanel([strings valueForStringKey:"Goodbye"],
  453.                 [strings valueForStringKey:"Enjoyed"],
  454.                 [strings valueForStringKey:"NoForget"], NULL, NULL);
  455.         } else {
  456.             NXRunAlertPanel([strings valueForStringKey:"Goodbye"],
  457.                 [strings valueForStringKey:"TryMe"],
  458.                 [strings valueForStringKey:"TryLater"], NULL, NULL);
  459.         }
  460.     }
  461.     [highScoreController closeServers]; // make sure server knows that we left.
  462.     return [NXApp terminate:sender];
  463. }
  464.  
  465. - windowDidResginMain:sender    // do pause if window loses main status
  466. {
  467.     [self pause];
  468.     return self;    // pause game
  469. }
  470.  
  471. - windowDidResignKey:sender        // do pause if window loses key status
  472. {
  473.     [self pause];
  474.     return self;    // pause game
  475. }
  476.  
  477. - windowDidBecomeKey:sender        // do unpause if window gains key status 
  478. {
  479.     [gameWindow makeFirstResponder:gameScreen];
  480.     if ([preferencesBrain autoUnPause]) [self unpause];    // unpause on unhide*/
  481.     return self;
  482. }
  483.  
  484. - windowDidDeminiaturize:sender        // clean off crap left by the new 
  485. {                                    // border animation in 3.0.  yuck!
  486.     [gameScreen update];
  487.     [[infoController niftyView] update];
  488.     return self;
  489. }
  490.  
  491. - windowDidMove:sender        // move status with game window 
  492. {
  493.     NXRect gameFrame, statsFrame;
  494.     
  495.     [gameWindow getFrame:&gameFrame];
  496.     [[levelText window] getFrame:&statsFrame];
  497.     [[levelText window] moveTo:(NX_X(&gameFrame) - NX_WIDTH(&statsFrame) + 1)
  498.         :NX_Y(&gameFrame) + NX_HEIGHT(&gameFrame) - NX_HEIGHT(&statsFrame)];
  499.     return self;
  500. }
  501.  
  502.  
  503. @end
  504.